package cn.wind.xboot.core.support.fileupload;

import cn.hutool.core.io.FileUtil;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author xukk
 */
@Slf4j
@Setter
public class FileUploadUtil {
    /**
     * 当上传文件超过限制时设定的临时文件位置，注意是绝对路径
     */
    private String tempPath = null;
    /**
     * 文件上传目标目录，注意是绝对路径
     */
    private String dstPath = null;
    /**
     * 新文件名称，不设置时默认为原文件名
     */
    private String newFileName = null;
    /**
     * 获取的上传请求
     */
    private HttpServletRequest fileuploadReq = null;
    /**
     * 设置最多只允许在内存中存储的数据,单位:字节，这个参数不要设置太大
     */
    private int sizeThreshold = 4096;
    /**
     * 设置允许用户上传文件大小,单位:字节
     * 共10M
     */
    private long sizeMax = 10485760;
    /**
     * 图片文件序号
     */
    private int picSeqNo = 1;
    /**
     * 设置此上传文件是否是缩略图文件，这个参数主要用于缩略图命名
     */
    private boolean isSmallPic = false;

    /**
     * 过滤掉的文件类型
     */
    private String[] errorType = {".exe", ".com", ".cgi", ".asp", ".php", ".jsp"};

    public FileUploadUtil() {
    }

    public FileUploadUtil(String tempPath, String destinationPath) {
        this.tempPath = tempPath;
        this.dstPath = destinationPath;
    }

    public FileUploadUtil(String tempPath, String destinationPath, HttpServletRequest fileuploadRequest) {
        this.tempPath = tempPath;
        this.dstPath = destinationPath;
        this.fileuploadReq = fileuploadRequest;
    }

    /**
     * 文件上载
     *
     * @return true —— success; false —— fail.
     */
    public boolean Upload() {
        DiskFileItemFactory factory = new DiskFileItemFactory();

        try {

            // 如果没有上传目的目录，则创建它

            FileUtil.mkdir(dstPath + "/upload");
            /*
			 * if (!FileUtil.makeDirectory(dstPath+"/ddd")) { throw new
			 * IOException("Create destination Directory Error."); }
			 */
            // 如果没有临时目录，则创建它
            FileUtil.mkdir(tempPath + "/temp");
			/*
			 * if (!FileUtil.makeDirectory(tempPath+"/ddd")) { throw new
			 * IOException("Create Temp Directory Error."); }
			 */

            // 上传项目只要足够小，就应该保留在内存里。
            // 较大的项目应该被写在硬盘的临时文件上。
            // 非常大的上传请求应该避免。
            // 限制项目在内存中所占的空间，限制最大的上传请求，并且设定临时文件的位置。

            // 设置最多只允许在内存中存储的数据,单位:字节
            factory.setSizeThreshold(sizeThreshold);
            // the location for saving data that is larger than
            // getSizeThreshold()
            factory.setRepository(new File(tempPath));

            ServletFileUpload upload = new ServletFileUpload(factory);
            // 设置允许用户上传文件大小,单位:字节
            upload.setSizeMax(sizeMax);

            List fileItems = upload.parseRequest(fileuploadReq);
            // assume we know there are two files. The first file is a small
            // text file, the second is unknown and is written to a file on
            // the server
            Iterator iterator = fileItems.iterator();

            // 正则匹配，过滤路径取文件名
            String regExp = "([^<>/\\\\|:\"\\*\\?]+)\\.\\w+$";
            Pattern p = Pattern.compile(regExp);
            while (iterator.hasNext()) {
                FileItem item = (FileItem) iterator.next();
                // 忽略其他不是文件域的所有表单信息
                if (!item.isFormField()) {
                    String name = item.getName();
                    log.info("++++=====" + name);
                    long size = item.getSize();
                    // 有多个文件域时，只上传有文件的
                    if (StringUtils.isBlank(name) && size == 0) {
                        continue;
                    }
                    Matcher m = p.matcher(name);
                    boolean result = m.find();
                    if (result) {
                        String ext = "." + FileUtil.extName(name);
                        for (int temp = 0; temp < errorType.length; temp++) {
                            if (ext.endsWith(errorType[temp])) {
                                throw new IOException(name + ": Wrong File Type");
                            }
                        }
                        try {
                            // 保存上传的文件到指定的目录
                            // 在下文中上传文件至数据库时，将对这里改写
                            // 没有指定新文件名时以原文件名来命名
                            if (StringUtils.isBlank(newFileName)) {
                                item.write(new File(dstPath + "/" + m.group()));
                            } else {
                                String uploadFilename;
                                if (isSmallPic) {
                                    uploadFilename = dstPath + "/" + newFileName + "_" + picSeqNo + "_small" + ext;
                                } else {
                                    uploadFilename = dstPath + "/" + newFileName + "_" + picSeqNo + ext;
                                }
                                // 生成所有未生成的目录
                                log.info("++++=====" + uploadFilename);
                                FileUtil.mkdir(uploadFilename);
                                // item.write(new File(dstPath +"/"+
                                // newFileName));
                                item.write(new File(uploadFilename));
                            }
                            picSeqNo++;
                        } catch (Exception e) {
                            throw new IOException(e.getMessage());
                        }
                    } else {
                        throw new IOException("fail to upload");
                    }
                }
            }
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        } catch (FileUploadException e) {
            log.error(e.getMessage(), e);
        }
        return true;
    }

    /**
     * 从路径中获取单独文件名
     *
     * @author
     */
    public String GetFileName(String filepath) {
        String returnstr = "*.*";
        int length = filepath.trim().length();

        filepath = filepath.replace('\\', '/');
        if (length > 0) {
            int i = filepath.lastIndexOf("/");
            if (i >= 0) {
                filepath = filepath.substring(i + 1);
                returnstr = filepath;
            }
        }
        return returnstr;
    }


}